"""
配置管理模块 - 完整修复版本
负责应用程序配置的加载、保存和管理
"""

import json
import os
import logging
import shutil
from typing import Dict, Any, Optional, List,Tuple
from datetime import datetime

logger = logging.getLogger(__name__)


class ConfigManager:
    """配置管理器 - 完整修复版本"""

    def __init__(self, config_file: str = "app_config.json"):
        # 使用绝对路径，避免相对路径问题
        if os.path.isabs(config_file):
            self.config_file = config_file
        else:
            # 存储在用户配置目录
            config_dir = self._get_config_directory()
            self.config_file = os.path.join(config_dir, config_file)

        # 备份配置目录
        self.backup_dir = os.path.join(os.path.dirname(self.config_file), "backups")

        # 默认配置
        self.default_config = {
            "version": "2.0",
            "always_on_top": True,
            "click_type": "单击",
            "delay_after": 0.5,
            "input_method": "paste",
            "clear_before_input": True,
            "enter_after_input": False,
            "global_delay": 3,
            "global_interval": 0.5,
            "stop_on_error": True,
            "window_geometry": "700x650",
            "window_position": None,
            "recent_files": [],
            "max_recent_files": 10,
            "auto_save_config": True,
            "backup_config": True,
            "max_backups": 5,
            "language": "zh-CN",
            "theme": "default",
            "log_level": "INFO"
        }

    def _get_config_directory(self) -> str:
        """获取配置目录 - 跨平台支持"""
        try:
            # 优先使用用户配置目录
            if os.name == 'nt':  # Windows
                app_data = os.getenv('APPDATA')
                if app_data:
                    config_dir = os.path.join(app_data, 'AutoClicker')
                else:
                    config_dir = os.path.join(os.path.expanduser('~'), '.autoclicker')
            elif os.name == 'posix':  # Linux/Mac
                config_home = os.getenv('XDG_CONFIG_HOME')
                if config_home:
                    config_dir = os.path.join(config_home, 'autoclicker')
                else:
                    config_dir = os.path.join(os.path.expanduser('~'), '.config', 'autoclicker')
            else:
                config_dir = os.path.join(os.path.expanduser('~'), '.autoclicker')

            # 创建目录
            os.makedirs(config_dir, exist_ok=True)
            return config_dir

        except Exception as e:
            logger.warning(f"获取配置目录失败，使用当前目录: {e}")
            return os.path.dirname(os.path.abspath(__file__))

    def load_config(self) -> Dict[str, Any]:
        """加载配置文件 - 修复版本"""
        try:
            config = self.default_config.copy()

            if os.path.exists(self.config_file):
                # 备份当前配置文件
                if self._should_backup():
                    self._create_backup()

                with open(self.config_file, 'r', encoding='utf-8') as f:
                    user_config = json.load(f)

                # 验证配置
                if not self._validate_config_structure(user_config):
                    logger.warning("配置文件结构无效，使用默认配置")
                    return config

                # 合并配置，确保新版本有默认值
                config.update(user_config)

                # 迁移旧版本配置
                config = self._migrate_config(config)

                logger.info(f"配置加载成功: {self.config_file}")
            else:
                logger.info("配置文件不存在，使用默认配置")
                # 保存默认配置
                self._save_config_safe(config)

            # 验证配置值
            validated_config = self._validate_config_values(config)
            if validated_config != config:
                logger.warning("配置值验证失败，使用验证后的配置")
                self._save_config_safe(validated_config)
                return validated_config

            return config

        except json.JSONDecodeError as e:
            logger.error(f"配置文件JSON格式错误: {e}")
            # 恢复备份
            restored = self._restore_backup()
            if restored:
                return self.load_config()
            return self.default_config.copy()
        except Exception as e:
            logger.error(f"加载配置失败: {e}")
            return self.default_config.copy()

    def save_config(self, config: Dict[str, Any]) -> bool:
        """保存配置到文件 - 修复版本"""
        try:
            # 验证配置
            if not self._validate_config_structure(config):
                logger.error("配置结构验证失败")
                return False

            # 验证配置值
            validated_config = self._validate_config_values(config)
            if validated_config != config:
                logger.warning("配置值验证失败，保存验证后的配置")
                config = validated_config

            # 添加元数据
            config_with_meta = config.copy()
            config_with_meta['version'] = self.default_config['version']
            config_with_meta['last_saved'] = datetime.now().isoformat()

            return self._save_config_safe(config_with_meta)

        except Exception as e:
            logger.error(f"保存配置失败: {e}")
            return False

    def _save_config_safe(self, config: Dict[str, Any]) -> bool:
        """安全保存配置（处理文件操作异常）"""
        try:
            # 确保配置目录存在
            config_dir = os.path.dirname(self.config_file)
            if config_dir and not os.path.exists(config_dir):
                os.makedirs(config_dir, exist_ok=True)

            # 使用临时文件避免写入过程中断
            temp_file = self.config_file + '.tmp'
            with open(temp_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2, sort_keys=True)

            # 替换原文件
            if os.path.exists(self.config_file):
                os.replace(temp_file, self.config_file)
            else:
                os.rename(temp_file, self.config_file)

            logger.info(f"配置保存成功: {self.config_file}")
            return True

        except Exception as e:
            logger.error(f"安全保存配置失败: {e}")
            # 清理临时文件
            try:
                if os.path.exists(temp_file):
                    os.unlink(temp_file)
            except:
                pass
            return False

    def reset_config(self) -> Dict[str, Any]:
        """重置为默认配置 - 修复版本"""
        try:
            # 备份当前配置
            if self._should_backup():
                self._create_backup()

            # 保存默认配置
            default_config = self.default_config.copy()
            if self._save_config_safe(default_config):
                logger.info("配置重置成功")
                return default_config
            else:
                logger.error("配置重置失败")
                return self.default_config.copy()

        except Exception as e:
            logger.error(f"重置配置失败: {e}")
            return self.default_config.copy()

    def update_recent_files(self, config: Dict[str, Any], file_path: str) -> Dict[str, Any]:
        """更新最近文件列表 - 修复版本"""
        try:
            if not file_path or not os.path.exists(file_path):
                return config

            # 确保最近文件列表存在
            if 'recent_files' not in config:
                config['recent_files'] = []

            # 标准化文件路径
            file_path = os.path.abspath(file_path)

            # 移除重复项
            config['recent_files'] = [f for f in config['recent_files']
                                    if os.path.abspath(f) != file_path]

            # 添加到开头
            config['recent_files'].insert(0, file_path)

            # 限制数量
            max_files = config.get('max_recent_files', 10)
            config['recent_files'] = config['recent_files'][:max_files]

            # 移除不存在的文件
            config['recent_files'] = [f for f in config['recent_files']
                                    if os.path.exists(f)]

            return config

        except Exception as e:
            logger.error(f"更新最近文件列表失败: {e}")
            return config

    def clear_recent_files(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """清空最近文件列表"""
        config['recent_files'] = []
        return config

    def validate_config(self, config: Dict[str, Any]) -> Tuple[bool, List[str]]:
        """验证配置的有效性 - 修复版本"""
        errors = []

        try:
            # 验证数值类型配置
            numeric_ranges = {
                'delay_after': (0, 60),
                'global_delay': (0, 300),
                'global_interval': (0, 60),
                'max_recent_files': (1, 50),
                'max_backups': (1, 20)
            }

            for field, (min_val, max_val) in numeric_ranges.items():
                if field in config:
                    try:
                        value = float(config[field])
                        if not (min_val <= value <= max_val):
                            errors.append(f"{field} 必须在 {min_val} 到 {max_val} 之间")
                    except (ValueError, TypeError):
                        errors.append(f"{field} 必须是数字")

            # 验证布尔类型配置
            boolean_fields = [
                'always_on_top', 'clear_before_input', 'enter_after_input',
                'stop_on_error', 'auto_save_config', 'backup_config'
            ]
            for field in boolean_fields:
                if field in config and not isinstance(config[field], bool):
                    errors.append(f"{field} 必须是布尔值")

            # 验证枚举类型配置
            if 'click_type' in config and config['click_type'] not in ['单击', '双击', '右键点击']:
                errors.append("click_type 必须是 '单击', '双击' 或 '右键点击'")

            if 'input_method' in config and config['input_method'] not in ['paste', 'keyboard', 'image']:
                errors.append("input_method 必须是 'paste', 'keyboard' 或 'image'")

            if 'log_level' in config and config['log_level'] not in ['DEBUG', 'INFO', 'WARNING', 'ERROR']:
                errors.append("log_level 必须是 'DEBUG', 'INFO', 'WARNING' 或 'ERROR'")

            # 验证窗口几何
            if 'window_geometry' in config:
                geometry = config['window_geometry']
                if not isinstance(geometry, str) or 'x' not in geometry:
                    errors.append("window_geometry 格式无效")

            # 验证最近文件列表
            if 'recent_files' in config:
                if not isinstance(config['recent_files'], list):
                    errors.append("recent_files 必须是列表")
                else:
                    for i, file_path in enumerate(config['recent_files']):
                        if not isinstance(file_path, str):
                            errors.append(f"recent_files[{i}] 必须是字符串")

            return len(errors) == 0, errors

        except Exception as e:
            errors.append(f"验证过程中出错: {e}")
            return False, errors

    def _validate_config_structure(self, config: Dict[str, Any]) -> bool:
        """验证配置结构"""
        try:
            if not isinstance(config, dict):
                return False

            # 检查必需字段
            required_fields = ['version']
            for field in required_fields:
                if field not in config:
                    return False

            return True

        except Exception:
            return False

    def _validate_config_values(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """验证并修复配置值"""
        validated = self.default_config.copy()
        validated.update(config)

        # 修复数值范围
        numeric_limits = {
            'delay_after': (0, 60),
            'global_delay': (0, 300),
            'global_interval': (0, 60)
        }

        for field, (min_val, max_val) in numeric_limits.items():
            if field in validated:
                try:
                    value = float(validated[field])
                    if value < min_val:
                        validated[field] = min_val
                    elif value > max_val:
                        validated[field] = max_val
                except (ValueError, TypeError):
                    validated[field] = self.default_config[field]

        # 修复枚举值
        if validated.get('click_type') not in ['单击', '双击', '右键点击']:
            validated['click_type'] = self.default_config['click_type']

        if validated.get('input_method') not in ['paste', 'keyboard', 'image']:
            validated['input_method'] = self.default_config['input_method']

        # 修复布尔值
        boolean_fields = [
            'always_on_top', 'clear_before_input', 'enter_after_input',
            'stop_on_error', 'auto_save_config', 'backup_config'
        ]
        for field in boolean_fields:
            if field in validated and not isinstance(validated[field], bool):
                validated[field] = self.default_config[field]

        return validated

    def _migrate_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """迁移旧版本配置"""
        try:
            version = config.get('version', '1.0')

            if version == '1.0':
                # 从1.0迁移到2.0
                logger.info("迁移配置从1.0到2.0")

                # 添加新字段
                if 'stop_on_error' not in config:
                    config['stop_on_error'] = self.default_config['stop_on_error']

                if 'auto_save_config' not in config:
                    config['auto_save_config'] = self.default_config['auto_save_config']

                if 'backup_config' not in config:
                    config['backup_config'] = self.default_config['backup_config']

                if 'max_backups' not in config:
                    config['max_backups'] = self.default_config['max_backups']

                # 更新版本号
                config['version'] = '2.0'

            return config

        except Exception as e:
            logger.error(f"配置迁移失败: {e}")
            return config

    def _should_backup(self) -> bool:
        """判断是否应该创建备份"""
        try:
            config = self.load_config()
            return config.get('backup_config', True)
        except:
            return True

    def _create_backup(self) -> bool:
        """创建配置备份"""
        try:
            if not os.path.exists(self.config_file):
                return False

            # 确保备份目录存在
            os.makedirs(self.backup_dir, exist_ok=True)

            # 生成备份文件名
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_file = os.path.join(self.backup_dir, f"config_backup_{timestamp}.json")

            # 复制文件
            shutil.copy2(self.config_file, backup_file)

            # 清理旧备份
            self._cleanup_old_backups()

            logger.info(f"配置备份创建成功: {backup_file}")
            return True

        except Exception as e:
            logger.error(f"创建配置备份失败: {e}")
            return False

    def _cleanup_old_backups(self) -> None:
        """清理旧的备份文件"""
        try:
            if not os.path.exists(self.backup_dir):
                return

            # 获取所有备份文件
            backup_files = []
            for filename in os.listdir(self.backup_dir):
                if filename.startswith("config_backup_") and filename.endswith(".json"):
                    filepath = os.path.join(self.backup_dir, filename)
                    backup_files.append((filepath, os.path.getctime(filepath)))

            # 按创建时间排序
            backup_files.sort(key=lambda x: x[1], reverse=True)

            # 获取最大备份数量
            config = self.load_config()
            max_backups = config.get('max_backups', 5)

            # 删除多余的备份
            for filepath, _ in backup_files[max_backups:]:
                try:
                    os.unlink(filepath)
                    logger.info(f"删除旧备份: {filepath}")
                except Exception as e:
                    logger.warning(f"删除旧备份失败 {filepath}: {e}")

        except Exception as e:
            logger.error(f"清理旧备份失败: {e}")

    def _restore_backup(self) -> bool:
        """从备份恢复配置"""
        try:
            if not os.path.exists(self.backup_dir):
                return False

            # 获取最新的备份文件
            backup_files = []
            for filename in os.listdir(self.backup_dir):
                if filename.startswith("config_backup_") and filename.endswith(".json"):
                    filepath = os.path.join(self.backup_dir, filename)
                    backup_files.append((filepath, os.path.getctime(filepath)))

            if not backup_files:
                return False

            # 使用最新的备份
            latest_backup = max(backup_files, key=lambda x: x[1])[0]

            # 恢复备份
            shutil.copy2(latest_backup, self.config_file)

            logger.info(f"配置从备份恢复成功: {latest_backup}")
            return True

        except Exception as e:
            logger.error(f"从备份恢复配置失败: {e}")
            return False

    def get_config_info(self) -> Dict[str, Any]:
        """获取配置信息"""
        try:
            config = self.load_config()
            file_info = {
                'config_file': self.config_file,
                'file_exists': os.path.exists(self.config_file),
                'backup_dir': self.backup_dir,
                'backup_dir_exists': os.path.exists(self.backup_dir),
                'default_config_keys': list(self.default_config.keys()),
                'user_config_keys': list(config.keys())
            }

            if os.path.exists(self.config_file):
                file_info['file_size'] = os.path.getsize(self.config_file)
                file_info['modified_time'] = datetime.fromtimestamp(
                    os.path.getmtime(self.config_file)
                ).isoformat()

            return file_info

        except Exception as e:
            logger.error(f"获取配置信息失败: {e}")
            return {}

    def export_config(self, export_path: str) -> bool:
        """导出配置到指定路径"""
        try:
            config = self.load_config()

            # 确保导出目录存在
            export_dir = os.path.dirname(export_path)
            if export_dir and not os.path.exists(export_dir):
                os.makedirs(export_dir, exist_ok=True)

            with open(export_path, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)

            logger.info(f"配置导出成功: {export_path}")
            return True

        except Exception as e:
            logger.error(f"导出配置失败: {e}")
            return False

    def import_config(self, import_path: str) -> bool:
        """从指定路径导入配置"""
        try:
            if not os.path.exists(import_path):
                logger.error(f"导入文件不存在: {import_path}")
                return False

            with open(import_path, 'r', encoding='utf-8') as f:
                imported_config = json.load(f)

            # 验证导入的配置
            is_valid, errors = self.validate_config(imported_config)
            if not is_valid:
                logger.error(f"导入的配置无效: {errors}")
                return False

            # 保存导入的配置
            return self.save_config(imported_config)

        except Exception as e:
            logger.error(f"导入配置失败: {e}")
            return False